﻿Public Class Filter

  Private ConditionCounter As Integer  'считчик фильтров

  Public FieldsFilter As ArrayList = New ArrayList()  'массив полей для фильтра

  Public SelFilters As ArrayList  'выбранные фильтры
  Public flSetFilter As Boolean   'флаг выбора фильтра

  'пример заполнения ArrFilter:
  'Dim FClass As Dictionary(Of String, Object)

  'FClass = New Dictionary(Of String, Object)
  'FClass.Add("Title", "ФИО")
  'FClass.Add("Name", "FIO")
  'FClass.Add("Type", "string")
  'FClass.Add("Value", "") 'для комбобокс - Dictionary(Of Integer, String)
  'ArrFilter.Add(FClass)


  Private Sub Filter_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

    Call setSelFilters()

  End Sub


  ''' <summary>
  ''' Установка выбранных раннее фильтров
  ''' </summary>
  ''' <remarks></remarks>
  Private Sub setSelFilters()

    If SelFilters.Count = 0 Then

      Call addCondition()

    Else

      For Each selFItem As Dictionary(Of String, String) In SelFilters

        'ключи массива selFItem
        'Oper
        'Condition
        'NameField
        'Value

        Dim i As Integer
        Call addCondition()

        'выставляем Оператор
        If ConditionCounter > 1 Then
          Dim CBOper As ComboBox = getControlOper(ConditionCounter)
          For i = 0 To CBOper.Items.Count - 1
            If CBOper.Items(i) = selFItem("Oper") Then
              CBOper.SelectedIndex = i
              Exit For
            End If
          Next
        End If

        'выставляем наименование поля
        Dim CBNameField As ComboBox = getControlNameField(ConditionCounter)
        For i = 0 To CBNameField.Items.Count - 1
          If DirectCast(CBNameField.Items(i), ComboType).ID_Item = selFItem("NameField") Then
            CBNameField.SelectedIndex = i
            Exit For
          End If
        Next

        'выставляем условие
        Dim CBCondition As ComboBox = getControlCondition(ConditionCounter)
        For i = 0 To CBCondition.Items.Count - 1
          If CBCondition.Items(i) = selFItem("Condition") Then
            CBCondition.SelectedIndex = i
            Exit For
          End If
        Next

        'выставляем значение 
        Dim objValue As Object = getControlValue(ConditionCounter)

        If TypeOf objValue Is TextBox Then
          'если string
          DirectCast(objValue, TextBox).Text = selFItem("Value")

        ElseIf TypeOf objValue Is ComboBox Then
          'если combobox
          For i = 0 To DirectCast(objValue, ComboBox).Items.Count - 1
            If DirectCast(DirectCast(objValue, ComboBox).Items(i), ComboType).ID_Item = selFItem("Value") Then
              DirectCast(objValue, ComboBox).SelectedIndex = i
              Exit For
            End If
          Next

        ElseIf TypeOf objValue Is DateTimePicker Then
          'если date
          DirectCast(objValue, DateTimePicker).Value = selFItem("Value")
        End If

      Next

    End If

  End Sub


  ''' <summary>
  ''' Добавление условия
  ''' </summary>
  Private Sub addCondition()

    Dim CBOper As New ComboBox
    Dim CBNameField As New ComboBox
    Dim CBCondition As New ComboBox
    Dim CBValue As New TextBox
    Dim cmdDelCond As New Button

    ConditionCounter += 1

    Dim NumCond As Integer = getCountConditions() + 1

    CBOper.Name = "CBOper" & ConditionCounter
    CBOper.Tag = ConditionCounter
    CBOper.DropDownStyle = ComboBoxStyle.DropDownList
    CBOper.Width = 75
    CBOper.Left = 11

    If NumCond = 1 Then
      CBOper.Top = 3
    Else
      CBOper.Top = CBOper.Height * (NumCond - 1) + 6 * (NumCond - 1) + 3
    End If

    CBOper.Visible = True

    CBNameField.Name = "CBNameField" & ConditionCounter
    CBNameField.Tag = ConditionCounter
    CBNameField.DropDownStyle = ComboBoxStyle.DropDownList
    CBNameField.Width = 150
    CBNameField.Top = CBOper.Top
    CBNameField.Left = 92
    CBNameField.Visible = True

    CBCondition.Name = "CBCondition" & ConditionCounter
    CBCondition.Tag = ConditionCounter
    CBCondition.DropDownStyle = ComboBoxStyle.DropDownList
    CBCondition.Width = 150
    CBCondition.Top = CBOper.Top
    CBCondition.Left = 248
    CBCondition.Visible = True


    CBValue.Name = "CBValue" & ConditionCounter
    CBValue.Tag = ConditionCounter
    CBValue.Width = 150
    CBValue.Top = CBOper.Top
    CBValue.Left = 404
    CBValue.Visible = True


    cmdDelCond.Name = "cmdAddCond" & ConditionCounter
    cmdDelCond.Tag = ConditionCounter
    cmdDelCond.Top = CBOper.Top
    cmdDelCond.Left = CBValue.Left + CBValue.Width + 3
    cmdDelCond.Width = 21
    cmdDelCond.Height = 21
    cmdDelCond.Image = My.Resources.funnel__minus
    cmdDelCond.FlatStyle = FlatStyle.Popup
    cmdDelCond.Visible = True


    'добавление значений:
    ''оператор
    CBOper.Items.Add("И")
    CBOper.Items.Add("ИЛИ")
    If NumCond > 1 Then CBOper.SelectedIndex = 0

    ''Имя поля
    For Each Fitem As Dictionary(Of String, Object) In FieldsFilter
      Dim CBItem As New ComboType
      CBItem.ID_Item = Fitem("Name")
      CBItem.Name_Item = Fitem("Title")
      CBNameField.Items.Add(CBItem)
    Next


    'события:
    AddHandler CBNameField.SelectedIndexChanged, AddressOf CBNameField_SelectedIndexChanged


    'добавление контролов в панель
    PanelCondition.Controls.Add(CBOper)
    PanelCondition.Controls.Add(CBNameField)
    PanelCondition.Controls.Add(CBCondition)
    PanelCondition.Controls.Add(CBValue)

    ''условие
    'Call addTitleConditions(CBCondition)
    Call setCondition("string", ConditionCounter)

    If NumCond > 1 Then
      PanelCondition.Controls.Add(cmdDelCond)
      AddHandler cmdDelCond.Click, AddressOf cmdDelCond_Click
    Else
      cmdAddCond.Left = cmdDelCond.Left
      CBOper.Enabled = False
    End If

    cmdAddCond.Top = CBOper.Height * NumCond + 6 * NumCond + 3

    Call changePositionCond()

  End Sub


  ''' <summary>
  ''' Удаление элементов условия с указанным индексом
  ''' </summary>
  ''' <param name="IndexCond">Индекс условия</param>
  Private Sub delCondition(ByVal IndexCond As Integer)

    Dim i As Integer
    For i = 0 To PanelCondition.Controls.Count - 1

      If Val(PanelCondition.Controls(i).Tag) = IndexCond Then
        PanelCondition.Controls.RemoveAt(i)
        Call delCondition(IndexCond)
        Exit Sub
      End If

    Next

    'вызов процедуры корректировки положения элементов
    Call changePositionCond()

  End Sub


  ''' <summary>
  ''' Корректировка позиций элементов условий
  ''' </summary>
  ''' <remarks></remarks>
  Private Sub changePositionCond()

    Dim NumCond As Integer, k As Integer, i As Integer
    For i = 0 To PanelCondition.Controls.Count - 1
      If PanelCondition.Controls(i).Name.IndexOf("CBOper") > -1 Then

        If Val(PanelCondition.Controls(i).Tag) > 1 Then

          NumCond += 1
          PanelCondition.Controls(i).Top = PanelCondition.Controls(i).Height * NumCond + 6 * NumCond + 3 + PanelCondition.AutoScrollPosition.Y

          For k = 0 To PanelCondition.Controls.Count - 1
            If Val(PanelCondition.Controls(k).Tag) = Val(PanelCondition.Controls(i).Tag) And k <> i Then
              PanelCondition.Controls(k).Top = PanelCondition.Controls(i).Top
            End If
          Next

        End If

      End If
    Next

    NumCond += 1
    cmdAddCond.Top = cmdAddCond.Height * NumCond + 6 * NumCond + 3 + PanelCondition.AutoScrollPosition.Y

  End Sub

  ''' <summary>
  ''' Функция определения кол-ва условий
  ''' </summary>
  ''' <returns>Кол-во условий</returns>
  Private Function getCountConditions() As Integer

    Dim CountCond As Integer, i As Integer

    For i = 0 To PanelCondition.Controls.Count - 1
      If PanelCondition.Controls(i).Name.IndexOf("CBOper") > -1 Then
        CountCond += 1
      End If
    Next

    Return CountCond

  End Function


  'установка условий фильтра
  Private Sub setFilter()

    SelFilters = New ArrayList()
    Dim ItemFilter As Dictionary(Of String, String)

    Dim i As Integer

    For i = 0 To PanelCondition.Controls.Count - 1
      If PanelCondition.Controls(i).Name.IndexOf("CBOper") > -1 Then

        Dim NumField As Integer = PanelCondition.Controls(i).Tag
        Dim titleNameField As String = DirectCast(getControlNameField(NumField), ComboBox).Text

        If titleNameField <> "" Then

          ItemFilter = New Dictionary(Of String, String)
          ItemFilter.Add("Oper", DirectCast(PanelCondition.Controls(i), ComboBox).Text)
          ItemFilter.Add("Condition", DirectCast(getControlCondition(NumField), ComboBox).Text)


          'имена полей
          For Each Fitem As Dictionary(Of String, Object) In FieldsFilter
            If titleNameField = Fitem("Title") Then
              ItemFilter.Add("NameField", Fitem("Name"))
              Exit For
            End If
          Next


          'значение
          Dim objValue As Object = getControlValue(NumField)
          If TypeOf objValue Is TextBox Then
            ItemFilter.Add("Value", SSPD.DB.SetQuotes(DirectCast(objValue, TextBox).Text))
          ElseIf TypeOf objValue Is ComboBox Then
            Dim cbItem As ComboType = DirectCast(objValue, ComboBox).SelectedItem
            ItemFilter.Add("Value", cbItem.ID_Item)
          ElseIf TypeOf objValue Is DateTimePicker Then
            ItemFilter.Add("Value", SSPD.DB.SetQuotes(DirectCast(objValue, DateTimePicker).Value))
          End If

          SelFilters.Add(ItemFilter)

        End If

      End If
    Next

    flSetFilter = True

  End Sub


  Private Sub cmdDelCond_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)

    Call delCondition(Val(DirectCast(sender, Button).Tag))

  End Sub


  Private Sub cmdAddCond_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdAddCond.Click

    Call addCondition()

  End Sub


  Private Sub cmdOK_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdOK.Click

    Call setFilter()
    Me.Close()

  End Sub


  Private Sub cmdCancel_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdCancel.Click

    Me.Close()

  End Sub


  Private Sub CBNameField_SelectedIndexChanged(ByVal sender As Object, ByVal e As EventArgs)

    Call changeFieldType(sender)

  End Sub


  ''' <summary>
  ''' Смена типа контрола значения и настройка условий
  ''' </summary>
  ''' <param name="sender"></param>
  ''' <remarks></remarks>
  Private Sub changeFieldType(ByVal sender As Object)


    Dim TypeField As String = FieldsFilter(DirectCast(sender, ComboBox).SelectedIndex)("Type")
    Dim NumField As Integer = DirectCast(sender, ComboBox).Tag

    'получаем нужный контрол значений
    Dim objValue As Object = getControlValue(NumField)

    'если контрол не найден, выходим
    If TypeOf objValue Is String Then Exit Sub

    'размеры и положение
    Dim SizeObj As Size, LocationObj As Point
    If TypeOf objValue Is TextBox Then
      SizeObj = DirectCast(objValue, TextBox).Size
      LocationObj = DirectCast(objValue, TextBox).Location
    ElseIf TypeOf objValue Is ComboBox Then
      SizeObj = DirectCast(objValue, ComboBox).Size
      LocationObj = DirectCast(objValue, ComboBox).Location
    ElseIf TypeOf objValue Is DateTimePicker Then
      SizeObj = DirectCast(objValue, DateTimePicker).Size
      LocationObj = DirectCast(objValue, DateTimePicker).Location
    End If

    'наименование контрола
    Dim NameControl As String = "CBValue" & NumField
    Dim NOV As Object = ""

    'замена контрола
    If TypeField = "string" Then

      If TypeOf objValue Is TextBox Then Exit Sub

      NOV = New TextBox
      NOV.Visible = True
      NOV.Size = SizeObj
      NOV.Location = LocationObj
      NOV.Name = NameControl
      NOV.Tag = NumField

    ElseIf TypeField = "combobox" Then

      If TypeOf objValue Is ComboBox Then Exit Sub

      NOV = New ComboBox
      NOV.Visible = True
      NOV.Size = SizeObj
      NOV.Location = LocationObj
      NOV.DropDownStyle = ComboBoxStyle.DropDownList
      NOV.Name = NameControl
      NOV.Tag = NumField

      'добавление значений в комбо
      If TypeOf FieldsFilter(DirectCast(sender, ComboBox).SelectedIndex)("Value") Is Dictionary(Of Integer, String) Then

        Dim arrValue As Dictionary(Of Integer, String) = FieldsFilter(DirectCast(sender, ComboBox).SelectedIndex)("Value")
        Dim cbItem As ComboType

        Dim i As Integer
        For i = 0 To arrValue.Count - 1
          cbItem = New ComboType
          cbItem.Name_Item = arrValue(i)
          cbItem.ID_Item = i
          NOV.Items.Add(cbItem)
        Next

      End If


    ElseIf TypeField = "date" Then

      NOV = New DateTimePicker
      NOV.Visible = True
      NOV.Size = SizeObj
      NOV.Location = LocationObj
      NOV.Name = NameControl
      NOV.Tag = NumField
      NOV.Value = Now

    End If


    'если новый контрол определен добавляем, если нет выходим
    If TypeOf NOV Is String Then Exit Sub

    'удаляем контрол значений
    Call delControlValue(NumField)

    'добавляем новый контрол значений
    PanelCondition.Controls.Add(NOV)

    'меняем условия в зависимости от типа значений
    Call setCondition(TypeField, NumField)

  End Sub

  ''' <summary>
  ''' Установка условий в указанной строке фильтра по типу поля значения
  ''' </summary>
  ''' <param name="TypeValue">Тип поля значений</param>
  ''' <param name="NumField">Номер строки фильтра</param>
  Private Sub setCondition(ByVal TypeValue As String, ByVal NumField As Integer)

    Dim objCond As ComboBox = getControlCondition(NumField)

    objCond.Items.Clear()

    If TypeValue = "string" Then

      objCond.Items.Add("содержит")
      objCond.Items.Add("не содержит")
      objCond.Items.Add("равно")
      objCond.Items.Add("не равно")

      objCond.SelectedIndex = 0

    ElseIf TypeValue = "combobox" Then

      objCond.Items.Add("равно")
      objCond.Items.Add("не равно")

      objCond.SelectedIndex = 0

    ElseIf TypeValue = "date" Then

      objCond.Items.Add("равно")
      objCond.Items.Add("не равно")
      objCond.Items.Add("больше или равно")
      objCond.Items.Add("меньше или равно")

      objCond.SelectedIndex = 0

    End If


  End Sub


  ''' <summary>
  ''' Возвращает поле условия по номеру строки условий
  ''' </summary>
  ''' <param name="NumField">Номер строки фильтра</param>
  ''' <returns>Контрол для значений</returns>
  Private Function getControlCondition(ByVal NumField As Integer) As Object

    Dim i As Integer
    For i = 0 To PanelCondition.Controls.Count - 1
      If PanelCondition.Controls(i).Name.IndexOf("CBCondition") > -1 Then
        If PanelCondition.Controls(i).Tag = NumField Then
          Return PanelCondition.Controls(i)
          Exit Function
        End If
      End If
    Next

    Return ""

  End Function


  ''' <summary>
  ''' Возвращает поле значения по номеру строки условий
  ''' </summary>
  ''' <param name="NumField">Номер строки фильтра</param>
  ''' <returns>Контрол для значений</returns>
  Private Function getControlValue(ByVal NumField As Integer) As Object

    Dim i As Integer
    For i = 0 To PanelCondition.Controls.Count - 1
      If PanelCondition.Controls(i).Name.IndexOf("CBValue") > -1 Then
        If PanelCondition.Controls(i).Tag = NumField Then
          Return PanelCondition.Controls(i)
          Exit Function
        End If
      End If
    Next

    Return ""

  End Function


  ''' <summary>
  ''' Возвращает контрол имени поля по номеру строки условий
  ''' </summary>
  ''' <param name="NumField">Номер строки фильтра</param>
  ''' <returns>Контрол для имени поля</returns>
  Private Function getControlNameField(ByVal NumField As Integer) As Object

    Dim i As Integer
    For i = 0 To PanelCondition.Controls.Count - 1
      If PanelCondition.Controls(i).Name.IndexOf("CBNameField") > -1 Then
        If PanelCondition.Controls(i).Tag = NumField Then
          Return PanelCondition.Controls(i)
          Exit Function
        End If
      End If
    Next

    Return ""

  End Function

  ''' <summary>
  ''' Возвращает контрол оператор по номеру строки условий
  ''' </summary>
  ''' <param name="NumField">Номер строки фильтра</param>
  ''' <returns>Контрол для оператора</returns>
  Private Function getControlOper(ByVal NumField As Integer) As Object

    Dim i As Integer
    For i = 0 To PanelCondition.Controls.Count - 1
      If PanelCondition.Controls(i).Name.IndexOf("CBOper") > -1 Then
        If PanelCondition.Controls(i).Tag = NumField Then
          Return PanelCondition.Controls(i)
          Exit Function
        End If
      End If
    Next

    Return ""

  End Function



  ''' <summary>
  ''' Удаление контрола значения по номеру строки условий
  ''' </summary>
  ''' <param name="NumField">Номер строки условий</param>
  Private Sub delControlValue(ByVal NumField As Integer)

    Dim i As Integer
    For i = 0 To PanelCondition.Controls.Count - 1
      If PanelCondition.Controls(i).Name.IndexOf("CBValue") > -1 Then
        If PanelCondition.Controls(i).Tag = NumField Then
          PanelCondition.Controls.RemoveAt(i)
          Exit Sub
        End If
      End If
    Next

  End Sub


  Private Sub cmdClear_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdClear.Click

    Dim i As Integer
    For i = 1 To ConditionCounter
      Call delCondition(i)
    Next

    ConditionCounter = 0
    Call addCondition()

  End Sub

End Class
